package elephant.servers.consoleserver.termos;

import static java.lang.Integer.valueOf;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.sshd.common.Factory;
import org.apache.sshd.common.channel.PtyMode;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.Signal;
import org.apache.sshd.server.SignalListener;

import elephant.servers.consoleserver.command.SshdCommand;
import elephant.servers.consoleserver.command.container.ContainerSshdCommand;
import elephant.servers.consoleserver.command.resource.DBResourceCommand;
import elephant.servers.consoleserver.command.rpcserver.RPCServerSshdCommand;

/**
 * @author <a href="mailto:trygvis@java.no">Trygve Laugst&oslash;l</a>
 * @version $Id$
 */
public class CliRunnerCommandFactory implements Factory<Command> {
	//
    public Map<String, SshdCommand> commands;
    //
    public CliRunnerCommandFactory() {
    	commands=new LinkedHashMap<>();
    	registerCommand(new ContainerSshdCommand());
    	registerCommand(new RPCServerSshdCommand());
    	registerCommand(new DBResourceCommand());
    }
    //
    public void registerCommand(SshdCommand cmd){
		if(commands.containsKey(cmd.name)){
			throw new IllegalArgumentException("cmd :"+cmd.name+" already exists.");
		}
		commands.put(cmd.name, cmd);
	}
    //
    public Command create() {
        return new Command() {
            private InputStream stdin;
            private OutputStream stdout;
            private OutputStream stderr;
            private ExitCallback exitCallback;
            private ReplThread repl;
            //
            public void setInputStream(InputStream stdin) {
                this.stdin = stdin;
            }

            public void setOutputStream(OutputStream stdout) {
                this.stdout = stdout;
            }

            public void setErrorStream(OutputStream stderr) {
                this.stderr = stderr;
            }

            public void setExitCallback(ExitCallback exitCallback) {
                this.exitCallback = exitCallback;
            }
            //
            public void start(Environment e) throws IOException {
            	System.out.println("Command start e:"+e.toString());
            	repl = new ReplThread(
            			stdin, 
            			stdout,
                		stderr,toReadLineEnvironment(e), 
                		commands, 
                		new Runnable() {
		                    public void run() {
		                        exitCallback.onExit(0);
		                    }
		                }
            	);
                repl.start();
            }
            //
            public void destroy() {
            	System.out.println("Command destory");
            	ReadlineUtil.closeQuietly(stdin);
            	ReadlineUtil.closeQuietly(stdout);
            	ReadlineUtil.closeQuietly(stderr);
            	ReadlineUtil.closeSilently(repl);
            }
        };
    }
    //
    public static boolean getBoolean(Map<PtyMode, Integer> map, PtyMode mode) {
        Integer i = map.get(mode);

        return i != null && i == 1;
    }
    //
    private static Integer getInteger(Map<String, String> env, String key) {
        String s = env.get(key);

        if (s == null) {
            return null;
        }

        try {
            return valueOf(s);
        } catch (NumberFormatException e) {
            return null;
        }
    }
    //
    public static ReadLineEnvironment toReadLineEnvironment(Environment environment) {
        final Map<String, String> env = environment.getEnv();
        Map<PtyMode, Integer> ptyModes = environment.getPtyModes();
        String encoding = null;
        Integer erase = ptyModes.get(PtyMode.VERASE);
        String user=env.get(Environment.ENV_USER);
        //
        final ReadLineEnvironment readLineEnvironment = new ReadLineEnvironment(encoding, erase,
            getBoolean(ptyModes, PtyMode.ICRNL),
            getBoolean(ptyModes, PtyMode.OCRNL),
            getInteger(env, Environment.ENV_COLUMNS),
            getInteger(env, Environment.ENV_LINES),
            user);
        
        environment.addSignalListener(new SignalListener() {
            public void signal(Signal signal) {
                readLineEnvironment.onWindowChange(
                    getInteger(env, Environment.ENV_COLUMNS),
                    getInteger(env, Environment.ENV_LINES));
            }
        });
        return readLineEnvironment;
    }
}
